Skip to content

feat: add capability checker for feature-level permissions#1

Merged
n-hallberg merged 1 commit intoamadeni:mainfrom
echo-ex-void:feat/capabilities
Mar 13, 2026
Merged

feat: add capability checker for feature-level permissions#1
n-hallberg merged 1 commit intoamadeni:mainfrom
echo-ex-void:feat/capabilities

Conversation

@echo-ex-void
Copy link
Copy Markdown
Collaborator

What

Adds createCapabilityChecker() — a generic capability system for feature-level permissions.

Based on the capability pattern from impuls-kbb (capabilities/helpers.ts + capabilities/registry.ts), genericized for reuse across all Amadeni projects.

API

import { createCapabilityChecker } from '@amadeni/convex-lib';

const capabilities = createCapabilityChecker({
  registry: {
    'invoice.manage': {
      label: 'Manage invoices',
      category: 'invoices',
      defaultRoles: ['accountant'],
    },
    // ...
  },
  getOverride: async (ctx, key) => {
    return await ctx.db.query('capabilityOverrides')
      .withIndex('by_key', q => q.eq('key', key))
      .first();
  },
  adminRoles: ['admin'],  // optional, default: ['admin']
});

// Check single capability
if (await capabilities.has(ctx, user.role, 'invoice.manage')) { ... }

// Check all capabilities for a role (admin panels)
const all = await capabilities.checkAll(ctx, user.role);
// → { 'invoice.manage': true, 'client.create': false, ... }

// Access registry metadata
capabilities.keys;      // readonly string[]
capabilities.registry;  // full registry for UI rendering

Features

  • Registry-based defaults: Each capability defines which roles have it by default
  • DB overrides: Runtime overrides take precedence over registry defaults
  • Admin bypass: Configurable admin roles always have all capabilities
  • checkAll(): Bulk check for admin panels / capability overview UIs
  • Type-safe keys: Capability keys are typed from the registry

Tests

6 new tests covering admin bypass, default roles, DB overrides, custom admin roles, checkAll, and key exposure.

Adds two features:

1. createCapabilityChecker() - generic capability system for feature-level permissions
   - Registry-based default roles per capability
   - DB overrides that take precedence over defaults
   - Admin role bypass, checkAll() for bulk checks
   - Based on impuls-kbb capability pattern, genericized for reuse

2. publicQuery / publicMutation / publicAction - explicit unauthenticated builders
   - Thin wrappers around generic Convex builders
   - Makes intent explicit: 'this endpoint is intentionally public'
   - Enables CI auth-enforcement: flag raw _generated/server imports,
     allow publicQuery as the sanctioned bypass
@n-hallberg n-hallberg merged commit af07608 into amadeni:main Mar 13, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants